﻿/**
 * @license Highcharts JS v2.3.3 (2012-11-02)
 *
 * (c) 2009-2011 Gert Vaartjes
 *
 * License: www.highcharts.com/license
 */
 
/*global window, require, phantom, console, $, document, Image, Highcharts, clearTimeout, options */
 
(function () {
 
"use strict";
 
var config = {
 
/* define locations of mandatory javascript files */
HIGHCHARTS: 'highstock.js',
HIGHCHARTS_MORE: 'highcharts-more.js',
JQUERY: 'jquery-1.8.2.min.js'
 
},
/* Internal */
page = require('webpage').create(),
fs = require('fs'),
system = require('system'),
args,
HC = {
 
},
pick,
mapArguments,
scaleAndClipPage,
input,
constr,
callback,
width,
callbackStr,
optionsStr,
output,
outputExtension,
pdfOutput,
svg,
svgFile,
svgElem,
timer;
 
HC.imagesLoaded = 'Highcharts.images.loaded';
HC.optionsParsed = 'Highcharts.options.parsed';
HC.callbackParsed = 'Highcharts.callback.parsed';
window.imagesLoaded = false;
window.optionsParsed = false;
window.callbackParsed = false;
 
page.onConsoleMessage = function (msg) {
 
console.log(msg);
/*
 * Ugly hacks, but only way to get messages out of the 'page.evaluate()'
 * sandbox. If any, please contribute with improvements on this!
 */
if (msg === HC.imagesLoaded) {
 
window.imagesLoaded = true;
 
}
 
/* more ugly hacks, to check options or callback are properly parsed */
if (msg === HC.optionsParsed) {
 
window.optionsParsed = true;
 
}
 
if (msg === HC.callbackParsed) {
 
window.callbackParsed = true;
 
}
 
 
};
 
page.onAlert = function (msg) {
 
console.log(msg);
 
};
 
pick = function () {
 
var args = arguments, i, arg, length = args.length;
for (i = 0; i < length; i += 1) {
 
arg = args[i];
if (arg !== undefined && arg !== null && arg !== 'null' && arg != '0') {
 
return arg;
 
}
 
}
 
};
 
mapArguments = function () {
 
var map = {
 
},
i;
for (i = 0; i < system.args.length; i += 1) {
 
if (system.args[i].charAt(0) === '-') {
 
map[system.args[i].substr(1, i.length)] = system.args[i + 1];
 
}
 
}
return map;
 
};
 
/* scale and clip the page */
scaleAndClipPage = function (svg, pdf) {
 
/*param: svg: The scg configuration object
param: pdf: boolean, if true set papersize
*/
 
var zoom = 1,
pageWidth = pick(args.width, svg.width),
clipwidth,
clipheight;
 
if (parseInt(pageWidth, 10) === pageWidth) {
 
zoom = pageWidth / svg.width;
 
}
 
/* set this line when scale factor has a higher precedence
scale has precedence : page.zoomFactor = args.scale  ? zoom * args.scale : zoom;*/
 
/* args.width has a higher precedence over scaling, to not break backover compatibility */
page.zoomFactor = args.scale && args.width === undefined ? zoom * args.scale : zoom;
 
clipwidth = svg.width * page.zoomFactor;
clipheight = svg.height * page.zoomFactor;
 
/* define the clip-rectangle */
page.clipRect = {
 
top: 0,
left: 0,
width: clipwidth,
height: clipheight
 
};
 
/* for pdf we need a bit more paperspace in some cases for example (w:600,h:400), I don't know why.*/
if (pdf) {
 
page.paperSize = {
 width: clipwidth, height: clipheight + 2
};
 
}
 
};
 
/* get the arguments from the commandline and map them */
args = mapArguments();
 
if (args.length < 1) {
 
console.log('Usage: highcharts-convert.js -infile URL -outfile filename -scale 2.5 -width 300 -constr Chart -callback callback.js');
console.log('Commandline parameter width is used for scaling, not for creating the chart');
phantom.exit(1);
 
} else {
 
input = args.infile;
output = pick(args.outfile, "chart.png");
constr = pick(args.constr, 'Chart');
callback = args.callback;
width = args.width;
 
outputExtension = output.split('.').pop();
pdfOutput = outputExtension === 'pdf';
 
/* Decide to generate the page from javascript or to load from svg file. */
 
if (input.split('.').pop() === 'json') {
 
// We have a json file, -> go headless!
 
// load necessary libraries
page.injectJs(config.JQUERY);
page.injectJs(config.HIGHCHARTS);
page.injectJs(config.HIGHCHARTS_MORE);
 
// load options from file
if (input !== undefined) {
 
optionsStr = fs.read(input);
 
} else {
 
console.log('No options file specified!');
phantom.exit();
 
}
 
// load callback from file
if (callback !== undefined) {
 
callbackStr = fs.read(callback);
 
}
 
// load chart in page and return svg height and width
svg = page.evaluate(function (width, constr, optionsStr, callbackStr, pdfOutput) {
 
 
var imagesLoadedMsg = 'Highcharts.images.loaded', $container, chart,
nodes, nodeIter, elem, opacity;
 
// dynamic script insertion
function loadScript(varStr, codeStr) {
 
var $script = $('<script>').attr('type', 'text/javascript');
$script.html('var ' + varStr + ' = ' + codeStr);
document.getElementsByTagName("head")[0].appendChild($script[0]);
if (window[varStr] !== undefined) {
 
console.log('Highcharts.' + varStr + '.parsed');
 
}
 
}
 
// are all images loaded in time?
function logCounter(counter) {
 
counter -= 1;
if (counter < 1) {
 
console.log(imagesLoadedMsg);
 
}
 
}
 
function loadImages() {
 
// are images loaded?
var $images = $('svg image'), counter, i, img;
 
if ($images.length > 0) {
 
 
counter = $images.length;
 
for (i = 0; i < $images.length; i += 1) {
 
img = new Image();
img.onload = logCounter(counter);
/* force loading of images by setting the src attr.*/
img.src = $images[i].getAttribute('href');
 
}
 
} else {
 
// no images set property to all images
// loaded
console.log(imagesLoadedMsg);
 
}
 
}
 
if (optionsStr !== 'undefined') {
 
loadScript('options', optionsStr);
 
}
 
if (callbackStr !== 'undefined') {
 
loadScript('callback', callbackStr);
 
}
 
$(document.body).css('margin', '0px');
$container = $('<div>').appendTo(document.body);
$container.attr('id', 'container');
 
// disable animations
Highcharts.SVGRenderer.prototype.Element.prototype.animate = Highcharts.SVGRenderer.prototype.Element.prototype.attr;
 
if (!options.chart) {
 
options.chart = {
 
};
 
}
 
options.chart.renderTo = $container[0];
 
// check if witdh is set. Order of precedence:
// args.width, options.chart.width and 600px
 
// OLD. options.chart.width = width || options.chart.width || 600;
// Notice we don't use commandline parameter width here. Commandline parameter width is used for scaling.
options.chart.width = (options.exporting && options.exporting.sourceWidth) || options.chart.width || 600;
options.chart.height = (options.exporting && options.exporting.sourceHeight) || options.chart.height || 400;
 
 
 
chart = new Highcharts[constr](options, callback);
 
// ensure images are all loaded
loadImages();
 
if (pdfOutput) {
 
/*
 * remove stroke-opacity paths, Qt shows
 * them as fully opaque in the PDF
 */
nodes = document.querySelectorAll('*[stroke-opacity]');
 
for (nodeIter = 0; nodeIter < nodes.length; nodeIter += 1) {
 
elem = nodes[nodeIter];
opacity = elem.getAttribute('stroke-opacity');
elem.removeAttribute('stroke-opacity');
elem.setAttribute('opacity', opacity);
 
}
 
}
 
return {
 
//html: $container[0].firstChild.innerHTML,
html: $('div.highcharts-container')[0].innerHTML,
width: chart.chartWidth,
height: chart.chartHeight
 
};
 
 
}, width, constr, optionsStr, callbackStr, pdfOutput);
 
if (!window.optionsParsed) {
 
console.log('ERROR - the options variable was not available, contains the infile an syntax error? see' + input);
phantom.exit();
 
}
 
if (callback !== undefined && !window.callbackParsed) {
 
console.log('ERROR - the callback variable was not available, contains the callbackfile an syntax error? see' + callback);
phantom.exit();
 
}
 
try {
 
// save the SVG to output or convert to other formats
if (outputExtension === 'svg') {
 
svgFile = fs.open(output, "w");
// set in xlink namespace for images.
svgFile.write(svg.html.replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
.replace(/ href=/g, ' xlink:href=').replace(/<\/svg>.*?$/, '</svg>'));
svgFile.close();
phantom.exit();
 
} else {
 
// check every 50 ms if all images are loaded
window.setInterval(function () {
 
if (!window.imagesLoaded) {
 
console.log('loading images...');
 
} else {
 
console.log('done loading images');
scaleAndClipPage(svg, pdfOutput);
page.render(output);
clearTimeout(timer);
phantom.exit();
 
}
 
}, 50);
// we have a 3 second timeframe..
timer = window.setTimeout(function () {
 
phantom.exit();
 
}, 3000);
 
}
 
} catch (e) {
 
console.log(e);
 
}
 
} else {
 /* render page directly from svg file */
page.open(input, function (status) {
 
var nodeIter, opacity, elem, svg;
if (status !== 'success') {
 
console.log('Unable to load the address!');
phantom.exit();
 
} else {
 
svg = page.evaluate(function (pdfOutput) {
 
if (pdfOutput) {
 
/*
 * remove stroke-opacity paths, Qt shows them as
 * fully opaque in the PDF, replace attributes with
 * opacity
 */
var nodes = document.querySelectorAll('*[stroke-opacity]');
 
for (nodeIter = 0; nodeIter < nodes.length; nodeIter += 1) {
 
elem = nodes[nodeIter];
opacity = elem.getAttribute('stroke-opacity');
elem.removeAttribute('stroke-opacity');
elem.setAttribute('opacity', opacity);
 
}
 
}
 
svgElem = document.getElementsByTagName('svg')[0];
return {
 
width: svgElem.getAttribute("width"),
height: svgElem.getAttribute("height")
 
};
 
}, pdfOutput);
 
scaleAndClipPage(svg, pdfOutput);
page.render(output);
phantom.exit();
 
}
 
});
 
}
 
}
 
}());
